package com.sbl.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sbl.domain.FileBean;
import com.sbl.domain.User;
import com.sbl.exception.SysException;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.FormParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.io.*;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.UUID;


/**
 * @ResponseBody 加上了这个注解，返回的就是字符串了，对象会转化为json字符串。字符串还是字符串了..
 */
@Controller
@RequestMapping("/user")
public class UserController {


//================================***返回值分类***====================================
    /**
     * 返回String
     *      ModelAndView可以拆分成两部分，Model和View，SpringMVC中，
     *              Model我们可以直接在参数中指定，然后返回值是逻辑视图名
     * @param model
     * @return ：返回字符串，可以返回逻辑视图的名称，通过视图解析器解析为物理视图地址
     */
    @RequestMapping("/testString")
    public String testString(Model model){
        System.out.println("testString方法执行了...");
        // 模拟从数据库中查询出User对象
        User user = new User();
        user.setUsername("小龙");
        user.setPassword("18");
        // model对象(存入到requestScope中了)
        model.addAttribute("user",user); // 这是视图模型
        return "success";//逻辑视图名，表示去查找一个名为success的视图
    }

    /**
     * 是void
     * 请求转发一次请求，不用编写项目的名称
     *     通过HttpServletRequest实现
     */
    @RequestMapping("/testVoid")
    public void testVoid(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("testVoid方法执行了...");
        // 编写请求转发的程序
        // request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);

        // 重定向
//        response.sendRedirect(request.getContextPath()+"/index.jsp");

        // 设置中文乱码
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        // 直接会进行响应
        response.getWriter().print("你好");

        return;
    }
    /**
     * 使用关键字的方式进行转发或者重定向
     *      请求转发一次请求，不用编写项目的名称
     *      重定向是两次请求，使用关键字重定向，框架自动添加了项目路径
     *
     * @return
     */
    @RequestMapping("/testForwardOrRedirect")
    public String testForwardOrRedirect(){
        System.out.println("testForwardOrRedirect方法执行了...");

        // 请求的转发（url写实际视图路径），这是服务端的跳转
//         return "forward:/WEB-INF/pages/success.jsp";

        // 重定向redirect（如果重定向到jsp页面，该jsp页面不能卸载WEB-INF路径下，否则找不到）；收
        // 客户端收到302后，客户端浏览器再跳转|这个是客户端自己在跳转
        return "redirect:/index.jsp";
    }

    /**
     *  以上返回字符串的2种情况（1.跳转页面 2.重定向和请求转发），都是有特殊的含义
     *  如果一定要返回一个字符串，需要额外加一个注解：@ResponseBody，
     *    这个注解表示当前方法返回的值就是要展示出来的字符串
     */
    @RequestMapping(value = "/onlytestString",produces = "text/html;charset=utf-8")//解决中文乱码
    public @ResponseBody String testString(){
        System.out.println("testString 方法执行了,单纯的返回字符串...");
        return "redirect:/index.jsp ，哈哈哈"; //原样输出
    }
 //========================================================================================================================
    /**
     * 模拟异步请求响应
     * @RequestBody 注解的作用是获得请求体，键值对的形式(加入Jackson依赖后，可以将json字符串转化为对象，因此可以
     * 直接写对象接收。之前写的是String类型-自己再封装成对象)
     * @ResponseBody 将对象转化为json
     *          Springmvc 默认用 MappingJacksonHttpMessageConverter 对 json 数据进行转换，需要加入
     * jackson 的包
     *
     * 2. @RequestBody之后可以跟String类型，那么接受的还是 json格式的字符串。只不过，有jackson框架，可以直接写成对象类型，框架
     * 会自动的进行转换
     *
     *  ajax 中：请求体中会有json数据，请求这个url，数据就携带给了user
     */
    @RequestMapping("/testAjax")
    public @ResponseBody User testAjax(@RequestBody User user){
        System.out.println("testAjax方法执行了...");
        // 客户端发送ajax的请求，传的是json字符串，后端把json字符串封装到user对象中
        System.out.println(user);

//        ObjectMapper mapper = new ObjectMapper();
//        try {
//            User user1 = mapper.readValue(user, User.class);
//            System.out.println("字符串类型"+user1);
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
        // 做响应，模拟查询数据库
        user.setUsername("小娜");
        user.setPassword("456");
        // 做响应
        return user;
    }


    //亲后端不分离的开发，大部分请求下，返回ModelAndView，即数据模型+视图
    @RequestMapping({"/testModelAndView"})
    public ModelAndView testModelAndView() {
        //创建ModelAndView对象
        ModelAndView mv = new ModelAndView();
        System.out.println("testModelAndView方法执行了...");
        //模拟从数据库中查询到对象
        User user = new User();
        user.setUsername("小凤");
        user.setPassword("456");
        //把user对象存储到mv对象中，也会把user对象存储到request域对象中
        mv.addObject("user", user);
        //设置视图名称，视图解析器会根据名称跳转到该页面
        mv.setViewName("success");
        return mv;
    }


//================================***上传文件***====================================

    /**
     * 跨服务器文件上传
     * @return
     */
    @RequestMapping("/fileupload3")
    public String fileuoload3(MultipartFile upload)  {
        System.out.println("跨服务器文件上传...");

        // 定义上传文件服务器路径
        String path = "http://localhost:8080/uploads/";

        // 说明上传文件项
        // 获取上传文件的名称
        String filename = upload.getOriginalFilename();
        // 把文件的名称设置唯一值，uuid，里面有-，替换掉
        String uuid = UUID.randomUUID().toString().replace("-", "");
        filename = uuid+"_"+filename;

        // 创建客户端的对象
        Client client = Client.create();
        // 和图片服务器进行连接
        WebResource webResource = client.resource(path + filename);
        // 上传文件
        try {
            webResource.put(upload.getBytes());
        } catch (IOException e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
        ClientResponse c = client.resource("pic").get(ClientResponse.class);
        InputStream entityInputStream = c.getEntityInputStream();



        return "success";
    }

    /**
     * SpringMVC文件上传
     * @return
     * MultipartFile upload ，
     *
     * HttpServletRequest request 这个参数是，网络请求封装的。
     *
     * MultipartFile 其实是Spring对文件流进行了封装，能获取长度，能转换成byte数组，能获取输入流，能直接写入文件.
     * 包含二进制数据+文件名称
     */
    @RequestMapping("/fileUpload2")
    public String fileuoload2(HttpServletRequest request, MultipartFile upload) throws Exception {
        System.out.println("springmvc文件上传...");
        // 使用fileupload组件完成文件上传
        // 上传的位置
        String path = request.getSession().getServletContext().getRealPath("/uploads/");
        // 判断，该路径是否存在
        File file = new File(path);
        if(!file.exists()){
            // 创建该文件夹
            file.mkdirs();
        }

        // 获取原始上传文件的名称
        String filename = upload.getOriginalFilename();
        // 把文件的名称设置唯一值，uuid
        String uuid = UUID.randomUUID().toString().replace("-", "");
        filename = uuid+"_"+filename;
        // 完成文件上传
        upload.transferTo(new File(path,filename));

//        FileUtils.readFileToByteArray()
        return "success";
    }

    /**
     * 文件上传
     * @return
     */
    @RequestMapping("/fileUpload1")
    public String fileuoload1(HttpServletRequest request) throws Exception {
        System.out.println("文件上传...");

        // 使用fileupload组件完成文件上传
        // 上传的位置
        String path = request.getSession().getServletContext().getRealPath("/uploads/");
        // 判断，该路径是否存在
        File file = new File(path);
        if(!file.exists()){
            // 创建该文件夹
            file.mkdirs();
        }
        // 解析request对象，获取上传文件项(在Comments框架中)
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        // 解析request
        List<FileItem> items = upload.parseRequest(request);
        // 遍历
        for(FileItem item:items){
            // 进行判断，当前item对象是否是上传文件项
            if(item.isFormField()){
                // 说明普通表单向
            }else{
                // 说明上传文件项
                // 获取上传文件的名称
                String filename = item.getName();
                // 把文件的名称设置唯一值，uuid
                String uuid = UUID.randomUUID().toString().replace("-", "");
                filename = uuid+"_"+filename;
                // 完成文件上传
                item.write(new File(path,filename));
                // 删除临时文件
                item.delete();
            }
        }

        return "success";
    }



    //************************************文件下载************************************
    //下载
    @RequestMapping("/download1")
//    @Produces(MediaType.APPLICATION_OCTET_STREAM)//返回方式为流
    public @ResponseBody String  postFile(@RequestBody FileBean fileBean,HttpServletResponse response){ //读出文件，返回文件流，下载
        byte[] a =null ;
        String s = null;
        String filepath = "D:\\"+fileBean.getFileName()+"";
        File file2 = new File(filepath);

//        byte[]
//        try {
//            String s = FileUtils.readFileToString(file2);
//            a =  FileUtils.readFileToByteArray(file2);
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        return  a ;


//        try {
//            s  = FileUtils.readFileToString(file2);
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        return  s ;
        // 配置文件下载
        if (filepath != null) {
            File file = new File(filepath);
            // 如果文件存在，则进行下载
            if (file.exists()) {
                // 配置文件下载
                response.setHeader("content-type", "application/octet-stream");
                response.setContentType("application/octet-stream");
                // 下载文件能正常显示中文
                try {
                    response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filepath, "UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }

                // 实现文件下载
                byte[] buffer = new byte[1024];
                FileInputStream fis = null;
                BufferedInputStream bis = null;
                try {
                    fis = new FileInputStream(file);
                    bis = new BufferedInputStream(fis);
                    //ServletOutputStream ,获取的是原生的输出流
                    OutputStream os = response.getOutputStream();

                    //3、读取和写入操作
//                  表示已经读取了多少个字符，如果是 -1，表示已经读取到文件的末尾
                    int n ;
                    while ( (n = bis.read(buffer,0,1024)) != -1){
                        System.out.println("time === "+n);
                        os.write(buffer,0,n);
                        os.flush();
                    }
                    System.out.println("Download  successfully!");
                    return "successfully";

                } catch (Exception e) {
                    System.out.println("Download  failed!");
                    return "failed";

                } finally {
                    if (bis != null) {
                        try {
                            bis.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (fis != null) {
                        try {
                            fis.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

        return "";

    }

    /**
     * 稿源周报excel表格下载
     * @return
     */

    @RequestMapping(value = "/downExcel", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
    @ResponseBody
    public String downExcel(HttpServletResponse response) throws UnsupportedEncodingException {
        LocalDate end = LocalDate.now();
        LocalDate start = end.minusDays(14);
        String filename = "稿源抓取周报-" + end.format(DateTimeFormatter.ISO_DATE) + ".xlsx";
        String filepath = "files/" + filename;
//        writeExcelFile(start, end, filepath);
        // 如果文件名不为空，则进行下载
        if (filename != null) {
            File file = new File(filepath);
            // 如果文件存在，则进行下载
            if (file.exists()) {
                // 配置文件下载
                response.setHeader("content-type", "application/octet-stream");
                response.setContentType("application/octet-stream;charset=UTF-8");
                response.setContentLength((int) file.length());
                // 下载文件能正常显示中文
                response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
                // 实现文件下载
                byte[] buffer = new byte[1024];
                FileInputStream fis = null;
                BufferedInputStream bis = null;
                try {
                    fis = new FileInputStream(file);
                    bis = new BufferedInputStream(fis);
                    //ServletOutputStream ,获取的是原生的输出流
                    OutputStream os = response.getOutputStream();
                    int i = bis.read(buffer);
                    while (i != -1) {
                        os.write(buffer, 0, i);
                        i = bis.read(buffer);
                    }
                    System.out.println("Download  successfully!");
                    return "successfully";

                } catch (Exception e) {
                    System.out.println("Download  failed!");
                    return "failed";

                } finally {
                    if (bis != null) {
                        try {
                            bis.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (fis != null) {
                        try {
                            fis.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return "";
    }



//================================***异常处理***====================================
//  浏览器-》前端控制器-》web(Controller) -》Servlet-》Dao
//  异常处理的原则就是抛出去，会不断的向上返回，
//           =》 最后交给前端处理器处理(HandlerExceptionResolver)  =》进而到达我们自定义的异常处理器

    /**
     *  在Controller中再抛出就到前端控制器了
     *      自定义前端控制器组件，在xml配置上，就可以使用了
     */
    @RequestMapping("/testException")
    public String testException() throws SysException {
        System.out.println("testException执行了...");
        try {
            // 模拟异常
            int a = 10/0;
        } catch (Exception e) {
            // 打印异常信息
            e.printStackTrace();
            // 抛出自定义异常信息
            throw new SysException("查询所有用户出现错误了...");
        }

        return "success";
    }




//================================***拦截器***====================================
    //测试拦截器效果
    @RequestMapping("/testInterceptor")
    public String testInterceptor(){
        System.out.println("testInterceptor  Controller中方法执行了...");
        return "success";
    }

}
